﻿/*  
	========================================================================
	 awzBoxes Library
	
	 Homepage: http://www.awzhome.de/
	========================================================================
	
	This Source Code Form is subject to the terms of the Mozilla Public
	License, v. 2.0. If a copy of the MPL was not distributed with this
	file, You can obtain one at http://mozilla.org/MPL/2.0/.
   
	The Original Code is code of awzBoxes Library.

	The Initial Developer of the Original Code is Andreas Weizel.
	Portions created by the Initial Developer are
	Copyright (C) 2011-2012 Andreas Weizel. All Rights Reserved.
	
	Contributor(s): (none)
	
	========================================================================
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Markup;
using AWZhome.awzBoxes.Tools;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Collections.ObjectModel;

namespace AWZhome.awzBoxes
{
	/// <summary>
	/// Interaction logic for BoxWindowContainer.xaml
	/// </summary>
	[ContentProperty("InnerAreaControl")]
	public partial class BoxWindowContainer : UserControl
	{
		#region Attached properties

		public static readonly DependencyProperty RowLayoutProperty =
			 DependencyProperty.RegisterAttached("RowLayout", typeof(bool), typeof(BoxWindowContainer), new UIPropertyMetadata(false));
		public static readonly DependencyProperty ColumnLayoutProperty =
			 DependencyProperty.RegisterAttached("ColumnLayout", typeof(bool), typeof(BoxWindowContainer), new UIPropertyMetadata(false));
		public static readonly DependencyProperty InnerAreaProperty =
			 DependencyProperty.RegisterAttached("InnerArea", typeof(bool), typeof(BoxWindowContainer), new UIPropertyMetadata(false));
		public static readonly DependencyProperty TopAutoHideAreaProperty =
			 DependencyProperty.RegisterAttached("TopAutoHideArea", typeof(bool), typeof(BoxWindowContainer), new UIPropertyMetadata(false));
		public static readonly DependencyProperty BottomAutoHideAreaProperty =
			 DependencyProperty.RegisterAttached("BottomAutoHideArea", typeof(bool), typeof(BoxWindowContainer), new UIPropertyMetadata(false));

		#endregion

		private SplittedColumnGrid _columnLayoutGrid;
		private SplittedRowGrid _rowLayoutGrid;
		private Grid _innerRowLayoutGrid;
		private Grid _innerColumnLayoutGrid;
		private BoxItem _openAutoHidePopupItem;

		private ItemsControl _topAutoHideButtonList;
		private ItemsControl _leftAutoHideButtonList;
		private ItemsControl _rightAutoHideButtonList;
		private ItemsControl _bottomAutoHideButtonList;

		private Control _innerAreaControl;
		private BoxArea _topBoxArea;
		private BoxArea _bottomBoxArea;
		private BoxArea _leftBoxArea;
		private BoxArea _rightBoxArea;

		private bool _isLoaded = false;

		private List<FloatingWindow> _floatingWindowsList;

		public static bool GetColumnLayout(DependencyObject obj)
		{
			return (bool) obj.GetValue(ColumnLayoutProperty);
		}

		public static void SetColumnLayout(DependencyObject obj, bool value)
		{
			obj.SetValue(ColumnLayoutProperty, value);
		}

		public static bool GetRowLayout(DependencyObject obj)
		{
			return (bool) obj.GetValue(RowLayoutProperty);
		}

		public static void SetRowLayout(DependencyObject obj, bool value)
		{
			obj.SetValue(RowLayoutProperty, value);
		}

		public static bool GetInnerArea(DependencyObject obj)
		{
			return (bool) obj.GetValue(InnerAreaProperty);
		}

		public static void SetInnerArea(DependencyObject obj, bool value)
		{
			obj.SetValue(InnerAreaProperty, value);
		}

		public static bool GetTopAutoHideArea(DependencyObject obj)
		{
			return (bool) obj.GetValue(TopAutoHideAreaProperty);
		}

		public static void SetTopAutoHideArea(DependencyObject obj, bool value)
		{
			obj.SetValue(TopAutoHideAreaProperty, value);
		}

		public static bool GetBottomAutoHideArea(DependencyObject obj)
		{
			return (bool) obj.GetValue(BottomAutoHideAreaProperty);
		}

		public static void SetBottomAutoHideArea(DependencyObject obj, bool value)
		{
			obj.SetValue(BottomAutoHideAreaProperty, value);
		}

		public BoxWindowContainer()
		{
			InitializeComponent();

			this.DataContext = this;

			// Save references to layout grids
			_leftAutoHideButtonList = this.leftAutoHideButtonList;
			_rightAutoHideButtonList = this.rightAutoHideButtonList;
			_columnLayoutGrid = this.ColumnLayoutGrid;
			_innerColumnLayoutGrid = (Grid) _columnLayoutGrid.Children[0];
			foreach (var childControl in _innerColumnLayoutGrid.Children)
			{
				if (BoxWindowContainer.GetRowLayout((DependencyObject) childControl))
				{
					_rowLayoutGrid = (SplittedRowGrid) childControl;
				}
				else if (BoxWindowContainer.GetTopAutoHideArea((DependencyObject) childControl))
				{
					_topAutoHideButtonList = (ItemsControl) childControl;
				}
				else if (BoxWindowContainer.GetBottomAutoHideArea((DependencyObject) childControl))
				{
					_bottomAutoHideButtonList = (ItemsControl) childControl;
				}
			}
			_innerRowLayoutGrid = (Grid) _rowLayoutGrid.Children.Single<FrameworkElement>(c => BoxWindowContainer.GetInnerArea(c));

			// Create list object for FloatingWindows
			_floatingWindowsList = new List<FloatingWindow>();

			// Configure auto-hide popup
			this.autoHidePopup.DataContext = null;

			// Handler for MouseDown event of popup ListBox
			boxItemPopupListBox.AddHandler(UIElement.MouseUpEvent,
				new MouseButtonEventHandler(boxItemPopupListBox_PreviewMouseLeftButtonUp), true);
		}

		/// <summary>
		/// Sets the control used for the inner area of the container.
		/// </summary>
		/// <remarks>
		/// If the user should be able to dock windows in the inner area.
		/// you have to set a <see cref="AWZhome.awzBoxes.BoxArea"/>
		/// as the inner area control.
		/// </remarks>
		public Control InnerAreaControl
		{
			get
			{
				return _innerAreaControl;
			}
			set
			{
				if (_innerAreaControl != null)
				{
					if (_innerAreaControl is BoxArea)
					{
						((BoxArea) _innerAreaControl).ParentBoxWindowContainer = this;
					}
					BindingOperations.ClearBinding(_innerRowLayoutGrid, Grid.VisibilityProperty);
					_innerRowLayoutGrid.Children.Remove(value);
				}
				if (value != null)
				{
					// Add the inner area control
					if (value is BoxArea)
					{
						BoxArea newBoxArea = (BoxArea) value;

						newBoxArea.ParentBoxWindowContainer = this;
						newBoxArea.Direction = BoxItemDockDirection.DockInnerArea;

						if (_isLoaded)
						{
							// Add an empty Box, if necessary
							if (newBoxArea.BoxLines.Count == 0)
							{
								newBoxArea.BoxLines.Add(new BoxLine());
							}
							BoxLine innerBoxLine = newBoxArea.BoxLines[0];
							if (innerBoxLine.Boxes.Count == 0)
							{
								innerBoxLine.Boxes.Add(new Box());
							}
						}
					}
					_innerRowLayoutGrid.SetBinding(Grid.VisibilityProperty, new Binding()
					{
						Source = value,
						Path = new PropertyPath("Visibility")
					});
					_innerRowLayoutGrid.Children.Add(value);
				}

				_innerAreaControl = value;
			}
		}

		/// <summary>
		/// Helper property for setting a <see cref="AWZhome.awzBoxes.BoxArea"/>
		/// as the inner area control.
		/// </summary>
		/// <remarks>
		/// The property will return <c>null</c>, if the inner area control is not a
		/// <see cref="AWZhome.awzBoxes.BoxArea"/>.
		/// </remarks>
		/// <seealso cref="AWZhome.awzBoxes.BoxWindowContainer.InnerAreaControl"/>
		public BoxArea InnerBoxArea
		{
			get
			{
				return this.InnerAreaControl as BoxArea;
			}
			set
			{
				this.InnerAreaControl = value;
			}
		}

		/// <summary>
		/// Sets a <see cref="AWZhome.awzBoxes.BoxArea"/>
		/// as the dock area at the top of the window.
		/// </summary>
		public BoxArea TopBoxArea
		{
			get
			{
				return _topBoxArea;
			}
			set
			{
				if (_topBoxArea != null)
				{
					_topBoxArea.ParentBoxWindowContainer = null;
					_topBoxArea.BoxLines.CollectionChanged -= topBoxArea_CollectionChanged;
				}

				_topBoxArea = value;

				if (value != null)
				{
					value.BoxLines.CollectionChanged += topBoxArea_CollectionChanged;
					value.AutoHiddenBoxes.CollectionChanged += AutoHiddenBoxes_CollectionChanged;
					value.Direction = BoxItemDockDirection.DockTop;
					value.ParentBoxWindowContainer = this;
				}
			}
		}

		private void topBoxArea_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
		{
			double? otherSideSize = this.BottomBoxHeight;

			if (_topBoxArea.BoxLines.Count > 0)
			{
				if (!_rowLayoutGrid.Children.Contains(_topBoxArea))
				{
					double areaSize = _rowLayoutGrid.GetRowSizes() / 3;

					// Add the new top BoxArea to SplittedRowGrid
					_rowLayoutGrid.Children.Insert(0, _topBoxArea);
					_rowLayoutGrid.SetRowHeight(0, areaSize, true);
				}
			}
			else
			{
				// Remove the element from Grid
				_rowLayoutGrid.Children.Remove(_topBoxArea);
			}

			// Prevent the grid from resizing the side-BoxArea on the other side
			if (otherSideSize.HasValue)
			{
				this.BottomBoxHeight = otherSideSize;
			}
		}

		/// <summary>
		/// Sets a <see cref="AWZhome.awzBoxes.BoxArea"/>
		/// as the dock area at the bottom of the window.
		/// </summary>
		public BoxArea BottomBoxArea
		{
			get
			{
				return _bottomBoxArea;
			}
			set
			{
				if (_bottomBoxArea != null)
				{
					_bottomBoxArea.ParentBoxWindowContainer = null;
					_bottomBoxArea.BoxLines.CollectionChanged -= bottomBoxArea_CollectionChanged;
				}

				_bottomBoxArea = value;

				if (value != null)
				{
					value.BoxLines.CollectionChanged += bottomBoxArea_CollectionChanged;
					value.AutoHiddenBoxes.CollectionChanged += AutoHiddenBoxes_CollectionChanged;
					value.Direction = BoxItemDockDirection.DockBottom;
					value.ParentBoxWindowContainer = this;
				}
			}
		}

		private void bottomBoxArea_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
		{
			double? otherSideSize = this.TopBoxHeight;

			if (_bottomBoxArea.BoxLines.Count > 0)
			{
				if (!_rowLayoutGrid.Children.Contains(_bottomBoxArea))
				{
					double areaSize = _rowLayoutGrid.GetRowSizes() / 3;

					// Add the new top BoxArea to SplittedRowGrid
					_rowLayoutGrid.Children.Add(_bottomBoxArea);
					_rowLayoutGrid.SetRowHeight(_rowLayoutGrid.Children.Count - 1, areaSize, true);
				}
			}
			else
			{
				// Remove the element from Grid
				_rowLayoutGrid.Children.Remove(_bottomBoxArea);
			}

			// Prevent the grid from resizing the side-BoxArea on the other side
			if (otherSideSize.HasValue)
			{
				this.TopBoxHeight = otherSideSize;
			}
		}

		/// <summary>
		/// Sets a <see cref="AWZhome.awzBoxes.BoxArea"/>
		/// as the dock area on the left side of the window.
		/// </summary>
		public BoxArea LeftBoxArea
		{
			get
			{
				return _leftBoxArea;
			}
			set
			{
				if (_leftBoxArea != null)
				{
					_leftBoxArea.ParentBoxWindowContainer = null;
					_leftBoxArea.BoxLines.CollectionChanged -= leftBoxArea_CollectionChanged;
				}

				_leftBoxArea = value;

				if (value != null)
				{
					// Add the new left BoxArea to SplittedColumnGrid
					value.BoxLines.CollectionChanged += leftBoxArea_CollectionChanged;
					value.AutoHiddenBoxes.CollectionChanged += AutoHiddenBoxes_CollectionChanged;
					value.Direction = BoxItemDockDirection.DockLeft;
					value.ParentBoxWindowContainer = this;
				}
			}
		}

		private void leftBoxArea_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
		{
			double? otherSideSize = this.RightBoxWidth;

			if (_leftBoxArea.BoxLines.Count > 0)
			{
				if (!_columnLayoutGrid.Children.Contains(_leftBoxArea))
				{
					double areaSize = _columnLayoutGrid.GetColumnSizes() / 3;

					// Add the new top BoxArea to SplittedColumnGrid
					_columnLayoutGrid.Children.Insert(0, _leftBoxArea);
					_columnLayoutGrid.SetColumnWidth(0, areaSize);
				}
			}
			else
			{
				// Remove the element from Grid
				_columnLayoutGrid.Children.Remove(_leftBoxArea);
			}

			// Prevent the grid from resizing the side-BoxArea on the other side
			if (otherSideSize.HasValue)
			{
				this.RightBoxWidth = otherSideSize;
			}
		}

		/// <summary>
		/// Sets a <see cref="AWZhome.awzBoxes.BoxArea"/>
		/// as the dock area on the right side of the window.
		/// </summary>
		public BoxArea RightBoxArea
		{
			get
			{
				return _rightBoxArea;
			}
			set
			{
				if (_rightBoxArea != null)
				{
					_rightBoxArea.ParentBoxWindowContainer = null;
					_rightBoxArea.BoxLines.CollectionChanged -= rightBoxArea_CollectionChanged;
				}

				_rightBoxArea = value;

				if (value != null)
				{
					// Add the new right BoxArea to SplittedColumnGrid
					value.BoxLines.CollectionChanged += rightBoxArea_CollectionChanged;
					value.AutoHiddenBoxes.CollectionChanged += AutoHiddenBoxes_CollectionChanged;
					value.Direction = BoxItemDockDirection.DockRight;
					value.ParentBoxWindowContainer = this;
				}
			}
		}

		private void rightBoxArea_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
		{
			double? otherSideSize = this.LeftBoxWidth;

			if (_rightBoxArea.BoxLines.Count > 0)
			{
				if (!_columnLayoutGrid.Children.Contains(_rightBoxArea))
				{
					double areaSize = _columnLayoutGrid.GetColumnSizes() / 3;

					// Add the new top BoxArea to SplittedColumnGrid
					_columnLayoutGrid.Children.Add(_rightBoxArea);
					_columnLayoutGrid.SetColumnWidth(_columnLayoutGrid.Children.Count - 1, areaSize);
				}
			}
			else
			{
				// Remove the element from Grid
				_columnLayoutGrid.Children.Remove(_rightBoxArea);
			}

			// Prevent the grid from resizing the side-BoxArea on the other side
			if (otherSideSize.HasValue)
			{
				this.LeftBoxWidth = otherSideSize;
			}
		}

		/// <summary>
		/// Gets/sets the height of the docking area at the top of the container.
		/// </summary>
		internal double? InnerBoxHeight
		{
			get
			{
				if (_rowLayoutGrid.Children.Contains(_innerRowLayoutGrid))
				{
					return _rowLayoutGrid.GetRowHeight(_rowLayoutGrid.Children.IndexOf(_innerRowLayoutGrid), false);
				}
				else
				{
					return null;
				}
			}
			set
			{
				if (value.HasValue && _rowLayoutGrid.Children.Contains(_innerRowLayoutGrid))
				{
					_rowLayoutGrid.SetRowHeight(_rowLayoutGrid.Children.IndexOf(_innerRowLayoutGrid), value.Value, true);
				}
			}
		}

		/// <summary>
		/// Gets/sets the height of the docking area on the left side of the container.
		/// </summary>
		internal double? InnerBoxWidth
		{
			get
			{
				if (_columnLayoutGrid.Children.Contains(_innerColumnLayoutGrid))
				{
					return _columnLayoutGrid.GetColumnWidth(_columnLayoutGrid.Children.IndexOf(_innerColumnLayoutGrid), false);
				}
				else
				{
					return null;
				}
			}
			set
			{
				if (value.HasValue && _columnLayoutGrid.Children.Contains(_innerColumnLayoutGrid))
				{
					_columnLayoutGrid.SetColumnWidth(_columnLayoutGrid.Children.IndexOf(_innerColumnLayoutGrid), value.Value);
				}
			}
		}

		/// <summary>
		/// Gets/sets the height of the docking area at the top of the container.
		/// </summary>
		public double? TopBoxHeight
		{
			get
			{
				if (_rowLayoutGrid.Children.Contains(_topBoxArea))
				{
					return _rowLayoutGrid.GetRowHeight(0, false);
				}
				else
				{
					return null;
				}
			}
			set
			{
				if (value.HasValue && _rowLayoutGrid.Children.Contains(_topBoxArea))
				{
					_rowLayoutGrid.SetRowHeight(0, value.Value, true);
				}
			}
		}

		/// <summary>
		/// Gets/sets the height of the docking area at the bottom of the container.
		/// </summary>
		public double? BottomBoxHeight
		{
			get
			{
				if (_rowLayoutGrid.Children.Contains(_bottomBoxArea))
				{
					return _rowLayoutGrid.GetRowHeight(_rowLayoutGrid.Children.Count - 1, false);
				}
				else
				{
					return null;
				}
			}
			set
			{
				if (value.HasValue && _rowLayoutGrid.Children.Contains(_bottomBoxArea))
				{
					_rowLayoutGrid.SetRowHeight(_rowLayoutGrid.Children.Count - 1, value.Value, true);
				}
			}
		}

		/// <summary>
		/// Gets/sets the height of the docking area on the left side of the container.
		/// </summary>
		public double? LeftBoxWidth
		{
			get
			{
				if (_columnLayoutGrid.Children.Contains(_leftBoxArea))
				{
					return _columnLayoutGrid.GetColumnWidth(0, false);
				}
				else
				{
					return null;
				}
			}
			set
			{
				if (value.HasValue && _columnLayoutGrid.Children.Contains(_leftBoxArea))
				{
					_columnLayoutGrid.SetColumnWidth(0, value.Value);
				}
			}
		}

		/// <summary>
		/// Gets/sets the height of the docking area on the right side of the container.
		/// </summary>
		public double? RightBoxWidth
		{
			get
			{
				if (_columnLayoutGrid.Children.Contains(_rightBoxArea))
				{
					return _columnLayoutGrid.GetColumnWidth(_columnLayoutGrid.Children.Count - 1, false);
				}
				else
				{
					return null;
				}
			}
			set
			{
				if (value.HasValue && _columnLayoutGrid.Children.Contains(_rightBoxArea))
				{
					_columnLayoutGrid.SetColumnWidth(_columnLayoutGrid.Children.Count - 1, value.Value);
				}
			}
		}

		/// <summary>
		/// Returns the array of open <see cref="AWZhome.awzBoxes.FloatingWindow"/>s.
		/// </summary>
		public FloatingWindow[] FloatingWindows
		{
			get
			{
				return _floatingWindowsList.ToArray();
			}
		}

		/// <summary>
		/// Returns the internal list of <see cref="AWZhome.awzBoxes.FloatingWindow"/> objects.
		/// </summary>
		internal List<FloatingWindow> FloatingWindowsList
		{
			get
			{
				return _floatingWindowsList;
			}
		}

		/// <summary>
		/// Checks whether this <see cref="AWZhome.awzBoxes.BoxWindowContainer"/> contains
		/// the given <see cref="AWZhome.awzBoxes.BoxItem"/>.
		/// </summary>
		/// <param name="boxItem">The BoxItem to check.</param>
		/// <returns><c>True</c> if the BoxItem is contained here, <c>false</c> otherwise.</returns>
		public bool ContainsBoxItem(BoxItem boxItem)
		{
			return (boxItem.ParentBoxWindowContainer == this);
		}

		/// <summary>
		/// Defines the storage provider used for saving and restoring the layout.
		/// </summary>
		public ILayoutStorageProvider LayoutStorageProvider
		{
			get;
			set;
		}

		/// <summary>
		/// Saves the current layout.
		/// </summary>
		public void SaveLayout()
		{
			if (this.LayoutStorageProvider == null)
			{
				// We can't save the layout without a storage provider
				return;
			}

			this.SaveLayout(null);

			// Set general configuration settings
			double innerWidth = this.InnerBoxWidth ?? Double.NaN;
			double innerHeight = this.InnerBoxHeight ?? Double.NaN;
			this.LayoutStorageProvider.SetGeneralConfiguration(
				"innerWidth", (Double.IsNaN(innerWidth) ? "-" : innerWidth.ToString()));
			this.LayoutStorageProvider.SetGeneralConfiguration(
				"innerHeight", (Double.IsNaN(innerHeight) ? "-" : innerHeight.ToString()));
		}

		/// <summary>
		/// Saves the current layout information of a single <see cref="AWZhome.awzBoxes.BoxItem"/>.
		/// </summary>
		/// <param name="boxItem">The BoxItem to save.</param>
		public void SaveLayout(BoxItem boxItem)
		{
			if (this.LayoutStorageProvider == null)
			{
				// We can't save the layout without a storage provider
				return;
			}

			// Run through all BoxItems
			List<string> openBoxItems = new List<string>();
			BoxArea[] openBoxAreas = new BoxArea[] {
				_innerAreaControl as BoxArea,
				_topBoxArea,
				_bottomBoxArea,
				_leftBoxArea,
				_rightBoxArea };

			foreach (BoxArea boxArea in openBoxAreas)
			{
				if (boxArea != null)
				{
					for (int lineIndex = 0; lineIndex < boxArea.BoxLines.Count; lineIndex++)
					{
						BoxLine boxLine = boxArea.BoxLines[lineIndex];
						if (boxLine != null)
						{
							for (int boxIndex = 0; boxIndex < boxLine.Boxes.Count; boxIndex++)
							{
								Box box = boxLine.Boxes[boxIndex];
								if (box != null)
								{
									for (int itemIndex = 0; itemIndex < box.Items.Count; itemIndex++)
									{
										BoxItem savedBoxItem = box.Items[itemIndex];
										if ((savedBoxItem != null) && ((boxItem == null) || (boxItem == savedBoxItem)))
										{
											// Create configuration
											char boxAreaDirection;
											double areaSize = Double.NaN;
											double areaSize2 = Double.NaN;
											switch (boxArea.Direction)
											{
												case BoxItemDockDirection.DockTop:
													boxAreaDirection = 't';
													areaSize = this.TopBoxHeight ?? Double.NaN;
													break;
												case BoxItemDockDirection.DockBottom:
													boxAreaDirection = 'b';
													areaSize = this.BottomBoxHeight ?? Double.NaN;
													break;
												case BoxItemDockDirection.DockLeft:
													boxAreaDirection = 'l';
													areaSize = this.LeftBoxWidth ?? Double.NaN;
													break;
												case BoxItemDockDirection.DockRight:
													boxAreaDirection = 'r';
													areaSize = this.RightBoxWidth ?? Double.NaN;
													break;
												default:
													boxAreaDirection = 'i';
													areaSize = this.InnerBoxWidth ?? Double.NaN;
													areaSize2 = this.InnerBoxHeight ?? Double.NaN;
													break;
											}

											double lineSize = boxArea.GetRowHeight(lineIndex, true);
											double boxSize = boxLine.GetColumnWidth(boxIndex, true);

											string itemconfig = String.Format(
													"1|{0}={1}={2}|{3}={4}={5}|{6}={7}={8}={9}|{10}={11}={12}",
													boxAreaDirection, // 0
													Double.IsNaN(areaSize) ? "-" : areaSize.ToString(), // 1
													Double.IsNaN(areaSize2) ? "-" : areaSize2.ToString(), // 2
													boxLine.ID.HasValue ? boxLine.ID.Value.ToString() : "-", // 3
													lineIndex, // 4
													Double.IsNaN(lineSize) ? "-" : lineSize.ToString(), // 5
													box.ID.HasValue ? box.ID.Value.ToString() : "-", // 6
													boxIndex, // 7
													0, // 8 (not needed, when not auto-hidden)
													Double.IsNaN(boxSize) ? "-" : boxSize.ToString(), // 9
													itemIndex, // 10
													0, // 11 (not needed, when not auto-hidden)
													Double.IsNaN(savedBoxItem.PopupSize) ? "-" : savedBoxItem.PopupSize.ToString() // 12
												);
											this.LayoutStorageProvider.SetBoxItemConfiguration(
												savedBoxItem.Name, itemconfig);

											if (savedBoxItem.IsRestored)
											{
												// Save in list of open BoxItems
												openBoxItems.Add(savedBoxItem.Name);
											}
										}
									}
								}
							}
						}
					}

					// Now run through all auto-hidden items in this BoxArea
					for (int autoHiddenBoxIndex = 0; autoHiddenBoxIndex < boxArea.AutoHiddenBoxes.Count; autoHiddenBoxIndex++)
					{
						AutoHiddenBox autoHiddenBox = boxArea.AutoHiddenBoxes[autoHiddenBoxIndex];
						for (int autoHideItemIndex = 0; autoHideItemIndex < autoHiddenBox.Items.Count; autoHideItemIndex++)
						{
							BoxItem savedBoxItem = autoHiddenBox.Items[autoHideItemIndex];
							if ((savedBoxItem != null) && ((boxItem == null) || (boxItem == savedBoxItem)))
							{
								// Create configuration
								string boxAreaDirection = null;
								switch (boxArea.Direction)
								{
									case BoxItemDockDirection.DockTop:
										boxAreaDirection = "ta";
										break;
									case BoxItemDockDirection.DockBottom:
										boxAreaDirection = "ba";
										break;
									case BoxItemDockDirection.DockLeft:
										boxAreaDirection = "la";
										break;
									case BoxItemDockDirection.DockRight:
										boxAreaDirection = "ra";
										break;
									default:
										break;
								}

								double areaSize = savedBoxItem.AutoHideStoredBoxAreaSize;
								double lineSize = savedBoxItem.AutoHideStoredBoxLineSize;
								double boxSize = savedBoxItem.AutoHideStoredBoxSize;

								string itemconfig = String.Format(
										"1|{0}={1}={2}|{3}={4}={5}|{6}={7}={8}={9}|{10}={11}={12}",
										boxAreaDirection, // 0
										Double.IsNaN(areaSize) ? "-" : areaSize.ToString(), // 1
										"-", // 2
										savedBoxItem.AutoHideStoredBoxLineID.HasValue ? savedBoxItem.AutoHideStoredBoxLineID.Value.ToString() : "-", // 3
										savedBoxItem.AutoHideStoredBoxLineIndex, // 4
										Double.IsNaN(lineSize) ? "-" : lineSize.ToString(), // 5
										savedBoxItem.AutoHideStoredBoxID.HasValue ? savedBoxItem.AutoHideStoredBoxID.Value.ToString() : "-", // 6
										savedBoxItem.AutoHideStoredBoxIndex, // 7
										autoHiddenBoxIndex, // 8
										Double.IsNaN(boxSize) ? "-" : boxSize.ToString(), // 9
										savedBoxItem.AutoHideStoredItemIndex, // 10
										autoHideItemIndex, // 11
										Double.IsNaN(savedBoxItem.PopupSize) ? "-" : savedBoxItem.PopupSize.ToString() // 12
									);
								this.LayoutStorageProvider.SetBoxItemConfiguration(
									savedBoxItem.Name, itemconfig);

								if (savedBoxItem.IsRestored)
								{
									// Save in list of open BoxItems
									openBoxItems.Add(savedBoxItem.Name);
								}
							}
						}
					}
				}
			}

			// Run through all floating windows
			for (int floatingWindowIndex = 0; floatingWindowIndex < this.FloatingWindowsList.Count; floatingWindowIndex++)
			{
				FloatingWindow floatingWindow = this.FloatingWindowsList[floatingWindowIndex];
				if (floatingWindow != null)
				{
					BoxItem savedBoxItem = floatingWindow.FloatingBoxItem;
					if ((savedBoxItem != null) && ((boxItem == null) || (boxItem == savedBoxItem)))
					{
						char direction;
						if (floatingWindow.WindowState == WindowState.Maximized)
						{
							direction = 'F';
						}
						else
						{
							direction = 'f';
						}

						string itemconfig = String.Format(
								"1|{0}|{1}|{2}|{3}|{4}|{5}",
								direction, // 0
								floatingWindowIndex, // 1
								floatingWindow.Top.ToString(), // 2
								floatingWindow.Left.ToString(), // 3
								floatingWindow.Height.ToString(), // 4
								floatingWindow.Width.ToString() // 5
							);
						this.LayoutStorageProvider.SetBoxItemConfiguration(
							savedBoxItem.Name, itemconfig);

						if (savedBoxItem.IsRestored)
						{
							// Save in list of open BoxItems
							openBoxItems.Add(savedBoxItem.Name);
						}
					}
				}
			}

			if (boxItem == null)
			{
				// Save complete list of open BoxItems
				this.LayoutStorageProvider.SaveLastOpenBoxItems(openBoxItems.ToArray());
			}
		}

		/// <summary>
		/// Restores the layout saved earlier using
		/// <see cref="AWZhome.awzBoxes.BoxWindowContainer.SaveLayout"/>.
		/// </summary>
		public void RestoreLayout()
		{
			if (this.LayoutStorageProvider == null)
			{
				// We can't restore the layout without a storage provider
				return;
			}

			// Get general configuration values
			double innerWidth = Double.NaN;
			double innerHeight = Double.NaN;
			string innerWidthValue = this.LayoutStorageProvider.GetGeneralConfiguration("innerWidth");
			string innerHeightValue = this.LayoutStorageProvider.GetGeneralConfiguration("innerHeight");
			if ((innerWidthValue != null) && (innerWidthValue != "-"))
			{
				Double.TryParse(innerWidthValue, out innerWidth);
			}
			if ((innerHeightValue != null) && (innerHeightValue != "-"))
			{
				Double.TryParse(innerHeightValue, out innerHeight);
			}
			this.InnerBoxWidth = innerWidth;
			this.InnerBoxHeight = innerHeight;

			// Get list of last open BoxItems
			string[] boxItemNames = this.LayoutStorageProvider.GetLastOpenBoxItems();
			if ((boxItemNames != null) && (boxItemNames.Length > 0))
			{
				// Add all restored BoxItems
				foreach (string boxItemName in boxItemNames)
				{
					this.RestoreBoxItem(boxItemName, false, false);
				}
				// As second step set the sizes
				foreach (string boxItemName in boxItemNames)
				{
					this.RestoreBoxItem(boxItemName, true, false);
				}
			}
		}

		/// <summary>
		/// Adds a <see cref="AWZhome.awzBoxes.BoxItem"/> to the container by trying
		/// to restore saved layout settings for it.
		/// </summary>
		/// <param name="boxItem">The BoxItem to add.</param>
		/// <remarks>
		/// The difference to manually adding the <see cref="AWZhome.awzBoxes.BoxItem"/> to
		/// a <see cref="AWZhome.awzBoxes.Box"/> is that this method first tries to restore
		/// the layout settings for the BoxItem and to add and size it appropriately. If
		/// no restoring is possible, the <see cref="AWZhome.awzBoxes.BoxItem.Direction"/>
		/// property is used to determine, where to dock the BoxItem.
		/// </remarks>
		public void AddBoxItem(BoxItem boxItem)
		{
			this.AddBoxItem(boxItem, false);
		}

		/// <summary>
		/// Adds a <see cref="AWZhome.awzBoxes.BoxItem"/> to the container by trying
		/// to restore saved layout settings for it.
		/// </summary>
		/// <param name="boxItem">The BoxItem to add.</param>
		/// <param name="restoreFromAutoHide">
		/// If <c>true</c>, information about the auto-hide mode of the item is ignored.
		/// This is used to restore items from auto-hide state.
		/// </param>
		/// <remarks>
		/// The difference to manually adding the <see cref="AWZhome.awzBoxes.BoxItem"/> to
		/// a <see cref="AWZhome.awzBoxes.Box"/> is that this method first tries to restore
		/// the layout settings for the BoxItem and to add and size it appropriately. If
		/// no restoring is possible, the <see cref="AWZhome.awzBoxes.BoxItem.Direction"/>
		/// property is used to determine, where to dock the BoxItem.
		/// </remarks>
		internal void AddBoxItem(BoxItem boxItem, bool restoreFromAutoHide)
		{
			if (this.LayoutStorageProvider != null)
			{
				if (this.RestoreBoxItem(boxItem, false, restoreFromAutoHide))
				{
					// Consider saved size
					this.RestoreBoxItem(boxItem, true, restoreFromAutoHide);
				}
				else
				{
					switch (boxItem.Direction)
					{
						case BoxItemDockDirection.DockTop:
							if (this.TopBoxArea != null)
							{
								boxItem.Redock(this.TopBoxArea);
							}
							break;
						case BoxItemDockDirection.DockBottom:
							if (this.BottomBoxArea != null)
							{
								boxItem.Redock(this.BottomBoxArea);
							}
							break;
						case BoxItemDockDirection.DockLeft:
							if (this.LeftBoxArea != null)
							{
								boxItem.Redock(this.LeftBoxArea);
							}
							break;
						case BoxItemDockDirection.DockRight:
							if (this.RightBoxArea != null)
							{
								boxItem.Redock(this.RightBoxArea);
							}
							break;
						case BoxItemDockDirection.DockInnerArea:
							if (this.InnerBoxArea != null)
							{
								boxItem.Redock(this.InnerBoxArea);
							}
							break;
						default:
							break;
					}
				}
			}
		}

		/// <summary>
		/// Returns a list of <see cref="AWZhome.awzBoxes.BoxItem"/> objects
		/// matching the given predicate.
		/// </summary>
		/// <param name="predicate">Predicate function.</param>
		/// <returns>Enumaration of BoxItems matching the given predicate.</returns>
		public IEnumerable<BoxItem> GetBoxItems(Func<BoxItem, bool> predicate)
		{
			if (predicate != null)
			{
				// Run through all BoxItems
				BoxArea[] openBoxAreas = new BoxArea[] {
				_innerAreaControl as BoxArea,
				_topBoxArea,
				_bottomBoxArea,
				_leftBoxArea,
				_rightBoxArea };

				foreach (BoxArea boxArea in openBoxAreas)
				{
					if (boxArea != null)
					{
						for (int lineIndex = 0; lineIndex < boxArea.BoxLines.Count; lineIndex++)
						{
							BoxLine boxLine = boxArea.BoxLines[lineIndex];
							if (boxLine != null)
							{
								for (int boxIndex = 0; boxIndex < boxLine.Boxes.Count; boxIndex++)
								{
									Box box = boxLine.Boxes[boxIndex];
									if (box != null)
									{
										for (int itemIndex = 0; itemIndex < box.Items.Count; itemIndex++)
										{
											BoxItem boxItem = box.Items[itemIndex];
											if ((boxItem != null) && (predicate(boxItem)))
											{
												yield return boxItem;
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}

		/// <summary>
		/// Attempts to restore a <see cref="AWZhome.awzBoxes.BoxItem"/> with
		/// its previously saved layout.
		/// </summary>
		/// <param name="boxItemName">Name of <see cref="AWZhome.awzBoxes.BoxItem"/> to restore.</param>
		/// <param name="sizeOnly">If <c>true</c>, only tries to restore sizes.</param>
		/// <param name="restoreFromAutoHide">
		/// If <c>true</c>, information about the auto-hide mode of the item is ignored.
		/// This is used to restore items from auto-hide state.
		/// </param>
		/// <returns>
		/// Instance of restored <see cref="AWZhome.awzBoxes.BoxItem"/> or <c>null</c>,
		/// if item could not be restored.
		/// </returns>
		private BoxItem RestoreBoxItem(string boxItemName, bool sizeOnly, bool restoreFromAutoHide)
		{
			BoxItem foundBoxItem = null;

			// First we look, whether the layout storage provider can restore the BoxItem
			if (this.LayoutStorageProvider != null)
			{
				//if (!sizeOnly)
				//{
				foundBoxItem = this.LayoutStorageProvider.RestoreBoxItem(boxItemName);
				//}
				this.RestoreBoxItem(foundBoxItem, sizeOnly, restoreFromAutoHide);
			}

			return foundBoxItem;
		}

		/// <summary>
		/// Attempts to restore the layout settings of a <see cref="AWZhome.awzBoxes.BoxItem"/>.
		/// </summary>
		/// <param name="boxItem">The BoxItem to restore the layout settings for.</param>
		/// <param name="sizeOnly">If <c>true</c>, only tries to restore sizes.</param>
		/// <param name="restoreFromAutoHide">
		/// If <c>true</c>, information about the auto-hide mode of the item is ignored.
		/// This is used to restore items from auto-hide state.
		/// </param>
		/// <returns>
		/// <c>True</c>, if the layout could be restored successfully,
		/// <c>false</c> otherwise.
		/// </returns>
		private bool RestoreBoxItem(BoxItem boxItem, bool sizeOnly, bool restoreFromAutoHide)
		{
			bool restored = false;

			// First we look, whether the layout storage provider can restore the BoxItem
			if ((this.LayoutStorageProvider != null) && (boxItem != null))
			{
				if (sizeOnly || (boxItem != null))
				{
					if (!sizeOnly && this.ContainsBoxItem(boxItem))
					{
						// Remove the BoxItem from its current container
						boxItem.Undock();
					}

					string boxItemConfig = this.LayoutStorageProvider.GetBoxItemConfiguration(boxItem.Name);
					if (!String.IsNullOrEmpty(boxItemConfig))
					{
						// Split the config string in several parts and check length and version info
						string[] confValues = boxItemConfig.Split('|');
						if ((confValues.Length >= 5) && (confValues[0] == "1"))
						{
							restored = true;

							BoxArea targetBoxArea = null;
							BoxLine targetBoxLine = null;
							Box targetBox = null;
							AutoHiddenBox targetAutoHiddenBox = null;
							bool isAutoHidden = false;

							if ((confValues.Length >= 7) && ((confValues[1] == "f") || (confValues[1] == "F")))
							{
								// Value describes position and size of a floating window
								int floatingWindowIndex = 0;
								int.TryParse(confValues[2], out floatingWindowIndex);

								double windowTop = Double.NaN;
								Double.TryParse(confValues[3], out windowTop);
								double windowLeft = Double.NaN;
								Double.TryParse(confValues[4], out windowLeft);
								double windowHeight = Double.NaN;
								Double.TryParse(confValues[5], out windowHeight);
								double windowWidth = Double.NaN;
								Double.TryParse(confValues[6], out windowWidth);

								FloatingWindow parentFloatingWindow = boxItem.ParentFloatingWindow;
								bool createdNewFloatingWindow = false;
								if (!sizeOnly && (parentFloatingWindow == null))
								{
									// Create a new FloatingWindow for the BoxItem
									parentFloatingWindow = new FloatingWindow(this, boxItem);
									createdNewFloatingWindow = true;
								}

								if (parentFloatingWindow != null)
								{
									parentFloatingWindow.Top = windowTop;
									parentFloatingWindow.Left = windowLeft;
									parentFloatingWindow.Height = windowHeight;
									parentFloatingWindow.Width = windowWidth;
									if (confValues[1] == "F")
									{
										parentFloatingWindow.WindowState = WindowState.Maximized;
									}
									else
									{
										parentFloatingWindow.WindowState = WindowState.Normal;
									}

									if (createdNewFloatingWindow)
									{
										parentFloatingWindow.Show();
									}
								}
							}
							else
							{
								// 2nd section: BoxArea
								string[] boxAreaConfig = confValues[1].Split('=');
								if ((boxAreaConfig != null) && (boxAreaConfig.Length == 3))
								{

									// Restoring a docked BoxItem
									Action<double, double> setAreaSize = null;
									switch (boxAreaConfig[0].Substring(0, 1))
									{
										case "t":
											targetBoxArea = this.TopBoxArea;
											setAreaSize = ((size, _ignored) => this.TopBoxHeight = size);
											break;
										case "b":
											targetBoxArea = this.BottomBoxArea;
											setAreaSize = ((size, _ignored) => this.BottomBoxHeight = size);
											break;
										case "l":
											targetBoxArea = this.LeftBoxArea;
											setAreaSize = ((size, _ignored) => this.LeftBoxWidth = size);
											break;
										case "r":
											targetBoxArea = this.RightBoxArea;
											setAreaSize = ((size, _ignored) => this.RightBoxWidth = size);
											break;
										case "i":
											targetBoxArea = this.InnerAreaControl as BoxArea;
											setAreaSize = ((width, height) =>
											{
												this.InnerBoxWidth = width;
												this.InnerBoxHeight = height;
											});
											break;
									}
									isAutoHidden = ((boxAreaConfig[0].Length > 1) && (boxAreaConfig[0][1] == 'a'));

									// Only treat the BoxItem as auto-hidden, if not restoring from auto-hidden state
									isAutoHidden = isAutoHidden && !restoreFromAutoHide;

									if (targetBoxArea != null)
									{
										bool resizeBoxArea = false;
										double areaSize = Double.NaN;
										double areaSize2 = Double.NaN;
										if (boxAreaConfig[1] != "-")
										{
											Double.TryParse(boxAreaConfig[1], out areaSize);
										}
										if (boxAreaConfig[2] != "-")
										{
											Double.TryParse(boxAreaConfig[2], out areaSize2);
										}

										if (isAutoHidden)
										{
											boxItem.AutoHideStoredBoxAreaSize = areaSize;
										}

										resizeBoxArea =
												(!Double.IsNaN(areaSize) && (targetBoxArea.BoxLines.Count == 0) && (setAreaSize != null));

										// 3rd section: BoxLine
										string[] boxLineConfig = confValues[2].Split('=');
										if ((boxLineConfig != null) && (boxLineConfig.Length == 3))
										{
											int? lineID = null;
											if (boxLineConfig[0] != "-")
											{
												int tempLineID;
												int.TryParse(boxLineConfig[0], out tempLineID);
												lineID = tempLineID;
											}
											int lineIndex = 0;
											int.TryParse(boxLineConfig[1], out lineIndex);
											double lineSize = Double.NaN;
											if (boxLineConfig[2] != "-")
											{
												Double.TryParse(boxLineConfig[2], out lineSize);
											}

											if (isAutoHidden)
											{
												boxItem.AutoHideStoredBoxLineIndex = lineIndex;
												boxItem.AutoHideStoredBoxLineSize = lineSize;
												boxItem.AutoHideStoredBoxLineID = lineID;
											}
											else
											{
												if (lineID.HasValue)
												{
													targetBoxLine = targetBoxArea.BoxLines.SingleOrDefault(l => l.ID == lineID);
												}
												if (targetBoxLine == null)
												{
													if (!sizeOnly)
													{
														targetBoxLine = new BoxLine();
														targetBoxLine.ID = lineID;
														if (targetBoxArea.BoxLines.Count <= lineIndex)
														{
															// We have not enough BoxLines, so simply add a new one
															targetBoxArea.BoxLines.Add(targetBoxLine);
														}
														else
														{
															targetBoxArea.BoxLines.Insert(lineIndex, targetBoxLine);
														}
													}
												}
												//else
												//{
												//    // Requested BoxLine already exists, use it
												//    targetBoxLine = targetBoxArea.BoxLines[lineIndex];
												//}

												if (sizeOnly && (targetBoxLine != null))
												{
													targetBoxArea.SetRowHeight(targetBoxArea.BoxLines.IndexOf(targetBoxLine), lineSize);
												}
											}
										}

										// After BoxLine has been added, resize the BoxArea
										if (resizeBoxArea)
										{
											setAreaSize(areaSize, areaSize2);
										}
									}

									// 4rd section: Box
									string[] boxConfig = confValues[3].Split('=');
									if ((boxConfig != null) && (boxConfig.Length == 4))
									{
										int? boxID = null;
										if (boxConfig[0] != "-")
										{
											int tempBoxID;
											int.TryParse(boxConfig[0], out tempBoxID);
											boxID = tempBoxID;
										}
										int boxIndex = 0;
										int.TryParse(boxConfig[1], out boxIndex);
										int autoHideBoxIndex = 0;
										int.TryParse(boxConfig[2], out autoHideBoxIndex);
										double boxSize = Double.NaN;
										if (boxConfig[3] != "-")
										{
											Double.TryParse(boxConfig[3], out boxSize);
										}

										if (isAutoHidden)
										{
											if (targetBoxArea != null)
											{
												boxItem.AutoHideStoredBoxIndex = boxIndex;
												boxItem.AutoHideStoredBoxSize = boxSize;
												boxItem.AutoHideStoredBoxID = boxID;

												if (targetBoxArea.AutoHiddenBoxes.Count <= autoHideBoxIndex)
												{
													// We have not enough AutoHiddenBoxes, so simply add a new one
													targetAutoHiddenBox = new AutoHiddenBox(targetBoxArea);
													targetBoxArea.AutoHiddenBoxes.Add(targetAutoHiddenBox);
												}
												else
												{
													// Requested AutoHiddenBox already exists, use it
													targetAutoHiddenBox = targetBoxArea.AutoHiddenBoxes[autoHideBoxIndex];
												}
											}
										}
										else
										{
											if (targetBoxLine != null)
											{
												if (boxID.HasValue)
												{
													targetBox = targetBoxLine.Boxes.SingleOrDefault(b => b.ID == boxID);
												}
												if (targetBox == null)
												{
													// The Box does not exist, create it
													if (!sizeOnly)
													{
														targetBox = new Box();
														targetBox.ID = boxID;
														if (targetBoxLine.Boxes.Count <= boxIndex)
														{
															// Not enough Boxes, add at the end
															targetBoxLine.Boxes.Add(targetBox);
														}
														else
														{
															// Insert the Box at definedplace
															targetBoxLine.Boxes.Insert(boxIndex, targetBox);
														}
													}
												}

												if (sizeOnly && (targetBox != null))
												{
													targetBoxLine.SetColumnWidth(targetBoxLine.Boxes.IndexOf(targetBox), boxSize);
												}
											}
										}
									}

									if (!sizeOnly)
									{
										// 5rd section: BoxItem
										string[] itemConfig = confValues[4].Split('=');
										if ((itemConfig != null) && (itemConfig.Length == 3))
										{
											int itemIndex = 0;
											int.TryParse(itemConfig[0], out itemIndex);
											int autoHideItemIndex = 0;
											int.TryParse(itemConfig[1], out autoHideItemIndex);

											if (isAutoHidden && (targetAutoHiddenBox != null))
											{
												boxItem.AutoHideStoredItemIndex = itemIndex;

												if (targetAutoHiddenBox.Items.Count <= autoHideItemIndex)
												{
													// We have not enough BoxItems, so simply add a new one
													targetAutoHiddenBox.Items.Add(boxItem);
												}
												else
												{
													// Insert the BoxItem
													targetAutoHiddenBox.Items.Insert(autoHideItemIndex, boxItem);
												}
											}
											else
											{
												if (targetBox != null)
												{
													if (targetBox.Items.Count <= itemIndex)
													{
														// We have not enough BoxItems, so simply add a new one
														targetBox.Items.Add(boxItem);
													}
													else
													{
														// Insert the BoxItem
														targetBox.Items.Insert(itemIndex, boxItem);
													}
												}
											}

											if (!restoreFromAutoHide)
											{
												double popupSize = Double.NaN;
												if (itemConfig[2] != "-")
												{
													Double.TryParse(itemConfig[2], out popupSize);
												}
												boxItem.PopupSize = popupSize;
											}
										}
									}
								}
							}
						}
					}
				}
			}

			return restored;
		}

		private void UserControl_Loaded(object sender, RoutedEventArgs e)
		{
			if (!_isLoaded)
			{
				// Handler for Ctrl(+Shift)+Tab
				Window parentWindow = ControlFinder.FindAncestor<Window>(this);
				if (parentWindow != null)
				{
					parentWindow.AddHandler(UIElement.KeyUpEvent, new KeyEventHandler(UserControl_KeyUp), true);
				}

				// Update all box areas when they become visible
				this.TopBoxArea = _topBoxArea;
				this.BottomBoxArea = _bottomBoxArea;
				this.LeftBoxArea = _leftBoxArea;
				this.RightBoxArea = _rightBoxArea;
				this.InnerAreaControl = _innerAreaControl;

				_isLoaded = true;
			}
		}

		private void UserControl_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
		{
			// Check for combination Ctrl+Tab
			if ((e.Key == Key.Tab)
				&& (Keyboard.IsKeyDown(Key.LeftCtrl)) || (Keyboard.IsKeyDown(Key.RightCtrl)))
			{
				if (!this.boxItemSelectorPopup.IsOpen)
				{
					// Bind list of open BoxItems in inner client area
					List<BoxItem> innerBoxItems = new List<BoxItem>(
						this.GetBoxItems(item => (item.Direction == BoxItemDockDirection.DockInnerArea)));
					Comparison<BoxItem> activityCountComparison = delegate(BoxItem boxItem1, BoxItem boxItem2)
					{
						if (boxItem1.ActivityCount > boxItem2.ActivityCount)
						{
							return -1;
						}
						else if (boxItem1.ActivityCount < boxItem2.ActivityCount)
						{
							return 1;
						}
						return 0;
					};
					innerBoxItems.Sort(activityCountComparison);
					if (innerBoxItems.Count() > 1)
					{
						this.boxItemPopupListBox.ItemsSource = innerBoxItems;
						this.boxItemPopupListBox.SelectedIndex = 1;
						this.boxItemPopupListBox.Focus();

						// Center the popup
						this.boxItemSelectorPopup.Placement = PlacementMode.Center;
						this.boxItemSelectorPopup.PlacementTarget = this;

						this.boxItemSelectorPopup.IsOpen = true;
					}
				}
				else
				{
					// The popup is already shown, switch between windows in list
					if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
					{
						// Move to previous window
						if (this.boxItemPopupListBox.SelectedIndex > 0)
						{
							this.boxItemPopupListBox.SelectedIndex--;
						}
						else
						{
							this.boxItemPopupListBox.SelectedIndex = this.boxItemPopupListBox.Items.Count - 1;
						}
					}
					else
					{
						// Move to next window
						if (this.boxItemPopupListBox.SelectedIndex < this.boxItemPopupListBox.Items.Count - 1)
						{
							this.boxItemPopupListBox.SelectedIndex++;
						}
						else
						{
							this.boxItemPopupListBox.SelectedIndex = 0;
						}
					}
				}

				e.Handled = true;
			}
			else if ((e.Key == Key.LeftCtrl) || (e.Key == Key.RightCtrl))
			{
				// Hide the selection window again
				this.boxItemSelectorPopup.IsOpen = false;

				// Activate selected BoxItem
				BoxItem activeBoxItem = this.boxItemPopupListBox.SelectedValue as BoxItem;
				if (activeBoxItem != null)
				{
					activeBoxItem.IsActive = true;
				}
				this.boxItemPopupListBox.ItemsSource = null;
			}
		}

		private void boxItemPopupListBox_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
		{
			// Hide the selection window again
			this.boxItemSelectorPopup.IsOpen = false;

			// Activate selected BoxItem
			BoxItem activeBoxItem = this.boxItemPopupListBox.SelectedValue as BoxItem;
			if (activeBoxItem != null)
			{
				activeBoxItem.IsActive = true;
			}
			this.boxItemPopupListBox.ItemsSource = null;
		}

		private void AutoHiddenBoxes_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
		{
			// Update visibility of auto-hide areas
			if (_topBoxArea != null)
			{
				if (_topBoxArea.AutoHiddenBoxes.Count > 0)
				{
					_topAutoHideButtonList.Visibility = System.Windows.Visibility.Visible;
				}
				else
				{
					_topAutoHideButtonList.Visibility = System.Windows.Visibility.Collapsed;
				}
			}

			if (_leftBoxArea != null)
			{
				if (_leftBoxArea.AutoHiddenBoxes.Count > 0)
				{
					_leftAutoHideButtonList.Visibility = System.Windows.Visibility.Visible;
				}
				else
				{
					_leftAutoHideButtonList.Visibility = System.Windows.Visibility.Collapsed;
				}
			}


			if (_rightBoxArea != null)
			{
				if (_rightBoxArea.AutoHiddenBoxes.Count > 0)
				{
					_rightAutoHideButtonList.Visibility = System.Windows.Visibility.Visible;
				}
				else
				{
					_rightAutoHideButtonList.Visibility = System.Windows.Visibility.Collapsed;
				}
			}

			if (_bottomBoxArea != null)
			{
				if (_bottomBoxArea.AutoHiddenBoxes.Count > 0)
				{
					_bottomAutoHideButtonList.Visibility = System.Windows.Visibility.Visible;
				}
				else
				{
					_bottomAutoHideButtonList.Visibility = System.Windows.Visibility.Collapsed;
				}
			}
		}

		private void BoxAutoHideTriggerCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = true;
		}

		private void BoxAutoHideTriggerCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			BoxItem boxItem = e.Parameter as BoxItem;
			if (boxItem != null)
			{
				if (boxItem.IsAutoHideOpen)
				{
					// Configure and open auto-hide popup
					this.autoHidePopup.DataContext = boxItem;
					_openAutoHidePopupItem = boxItem;
					switch (boxItem.Direction)
					{
						case BoxItemDockDirection.DockTop:
							this.autoHidePopup.Placement = PlacementMode.Relative;
							this.autoHidePopup.PlacementTarget = _topAutoHideButtonList;
							this.autoHidePopup.VerticalOffset = _topAutoHideButtonList.ActualHeight;
							this.autoHidePopup.HorizontalOffset = 0;
							this.autoHidePopup.Height = Double.IsNaN(boxItem.PopupSize) ? this.ActualHeight / 3 : boxItem.PopupSize;
							this.autoHidePopup.Width = _topAutoHideButtonList.ActualWidth;
							break;
						case BoxItemDockDirection.DockBottom:
							this.autoHidePopup.Placement = PlacementMode.Relative;
							this.autoHidePopup.PlacementTarget = _bottomAutoHideButtonList;
							this.autoHidePopup.Height = Double.IsNaN(boxItem.PopupSize) ? this.ActualHeight / 3 : boxItem.PopupSize;
							this.autoHidePopup.Width = _bottomAutoHideButtonList.ActualWidth;
							this.autoHidePopup.VerticalOffset = -this.autoHidePopup.Height;
							this.autoHidePopup.HorizontalOffset = 0;
							break;
						case BoxItemDockDirection.DockLeft:
							this.autoHidePopup.Placement = PlacementMode.Relative;
							this.autoHidePopup.PlacementTarget = _leftAutoHideButtonList;
							this.autoHidePopup.HorizontalOffset = _leftAutoHideButtonList.ActualWidth;
							this.autoHidePopup.VerticalOffset = 0;
							this.autoHidePopup.Height = _leftAutoHideButtonList.ActualHeight;
							this.autoHidePopup.Width = Double.IsNaN(boxItem.PopupSize) ? this.ActualWidth / 3 : boxItem.PopupSize;
							break;
						case BoxItemDockDirection.DockRight:
							this.autoHidePopup.Placement = PlacementMode.Relative;
							this.autoHidePopup.PlacementTarget = _rightAutoHideButtonList;
							this.autoHidePopup.Width = Double.IsNaN(boxItem.PopupSize) ? this.ActualWidth / 3 : boxItem.PopupSize;
							this.autoHidePopup.Height = _rightAutoHideButtonList.ActualHeight;
							this.autoHidePopup.HorizontalOffset = -this.autoHidePopup.Width;
							this.autoHidePopup.VerticalOffset = 0;
							break;
						default:
							break;
					}

					// Set visibility of resize thumbs
					this.autoHideTopThumb.Visibility =
						(boxItem.Direction == BoxItemDockDirection.DockBottom) ? Visibility.Visible : Visibility.Collapsed;
					this.autoHideLeftThumb.Visibility =
						(boxItem.Direction == BoxItemDockDirection.DockRight) ? Visibility.Visible : Visibility.Collapsed;
					this.autoHideRightThumb.Visibility =
						(boxItem.Direction == BoxItemDockDirection.DockLeft) ? Visibility.Visible : Visibility.Collapsed;
					this.autoHideBottomThumb.Visibility =
						(boxItem.Direction == BoxItemDockDirection.DockTop) ? Visibility.Visible : Visibility.Collapsed;

					if (boxItem.Content != null)
					{
						this.autoHidePopupContainer.Children.Add(boxItem.Content);
					}
					Binding popupBinding = new Binding()
					{
						Source = boxItem,
						Path = new PropertyPath("IsAutoHideOpen"),
						Mode = BindingMode.TwoWay
					};
					this.autoHidePopup.SetBinding(Popup.IsOpenProperty, popupBinding);
				}
			}
		}

		private void autoHideLeftThumb_DragDelta(object sender, DragDeltaEventArgs e)
		{
			double xadjust = this.autoHidePopup.Width - e.HorizontalChange;
			double xadjustOffset = this.autoHidePopup.HorizontalOffset + e.HorizontalChange;
			if (xadjust >= 0)
			{
				this.autoHidePopup.Width = xadjust;
				this.autoHidePopup.HorizontalOffset = xadjustOffset;
			}

			if (_openAutoHidePopupItem != null)
			{
				_openAutoHidePopupItem.PopupSize = this.autoHidePopup.Width;
			}
		}

		private void autoHideTopThumb_DragDelta(object sender, DragDeltaEventArgs e)
		{
			double yadjust = this.autoHidePopup.Height - e.VerticalChange;
			double yadjustOffset = this.autoHidePopup.VerticalOffset + e.VerticalChange;
			if (yadjust >= 0)
			{
				this.autoHidePopup.Height = yadjust;
				this.autoHidePopup.VerticalOffset = yadjustOffset;
			}

			if (_openAutoHidePopupItem != null)
			{
				_openAutoHidePopupItem.PopupSize = this.autoHidePopup.Height;
			}
		}

		private void autoHideRightThumb_DragDelta(object sender, DragDeltaEventArgs e)
		{
			double xadjust = this.autoHidePopup.Width + e.HorizontalChange;
			if (xadjust >= 0)
			{
				this.autoHidePopup.Width = xadjust;
			}

			if (_openAutoHidePopupItem != null)
			{
				_openAutoHidePopupItem.PopupSize = this.autoHidePopup.Width;
			}
		}

		private void autoHideBottomThumb_DragDelta(object sender, DragDeltaEventArgs e)
		{
			double yadjust = this.autoHidePopup.Height + e.VerticalChange;
			if (yadjust >= 0)
			{
				this.autoHidePopup.Height = yadjust;
			}

			if (_openAutoHidePopupItem != null)
			{
				_openAutoHidePopupItem.PopupSize = this.autoHidePopup.Height;
			}
		}

		private void AutoHideBoxItemCloseCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = true;
		}

		private void AutoHideBoxItemCloseCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			BoxItem boxItem = e.Parameter as BoxItem;
			if (boxItem != null)
			{
				// Close the popup and remove
				this.autoHidePopup.IsOpen = false;
				this.ResetAutoHidePopup();

				// Reset reference to the parent AutoHiddenBox
				boxItem.ParentAutoHiddenBox = null;

				// Close the BoxItem
				boxItem.Close();
			}
		}

		private void AutoHideBoxItemRedockCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
		{
			e.CanExecute = true;
		}

		private void AutoHideBoxItemRedockCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			BoxItem boxItem = e.Parameter as BoxItem;
			if (boxItem != null)
			{
				// Close the popup
				this.autoHidePopup.IsOpen = false;
				this.ResetAutoHidePopup();

				// Restore the BoxItems of the AutoHiddenBox on corresponding BoxArea
				if (boxItem.ParentAutoHiddenBox != null)
				{
					BoxArea boxArea = null;
					switch (boxItem.Direction)
					{
						case BoxItemDockDirection.DockTop:
							boxArea = _topBoxArea;
							break;
						case BoxItemDockDirection.DockBottom:
							boxArea = _bottomBoxArea;
							break;
						case BoxItemDockDirection.DockLeft:
							boxArea = _leftBoxArea;
							break;
						case BoxItemDockDirection.DockRight:
							boxArea = _rightBoxArea;
							break;
						default:
							break;
					}
					if (boxArea != null)
					{
						boxArea.RestoreAutoHiddenBox(boxItem.ParentAutoHiddenBox);
					}

					// Reset reference to the parent AutoHiddenBox
					boxItem.ParentAutoHiddenBox = null;
				}
			}
		}

		private void autoHidePopup_Closed(object sender, EventArgs e)
		{
			this.ResetAutoHidePopup();
		}

		private void ResetAutoHidePopup()
		{
			// Release the popup's bindings, empty control container
			this.autoHidePopupContainer.Children.Clear();
			BindingOperations.ClearBinding(this.autoHidePopup, Popup.IsOpenProperty);
			this.autoHidePopup.PlacementTarget = null;
			this.autoHidePopup.DataContext = null;
			_openAutoHidePopupItem = null;
		}
	}
}
